package org.glandais.profiler;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.glandais.profiler.beans.binary.MethodTrace;
import org.glandais.profiler.beans.binary.TraceStack;

public class DumpStatistics {

	private static final String EMPTY = ""; //$NON-NLS-1$
	private static final String SPACE = " "; //$NON-NLS-1$
	private static final String SEPARATOR = "************************************************"; //$NON-NLS-1$

	static final String[] tabs = new String[1000];

	static {
		StringBuilder sb = new StringBuilder(EMPTY);
		for (int i = 0; i < 1000; i++) {
			tabs[i] = sb.toString();
			sb.append(SPACE);
		}
	}

	private TraceStack traceStack;
	private Map<String, DumpEntry> map;

	private class DumpEntry {
		long value = 0;
		Map<String, DumpEntry> subMap = new HashMap<String, DumpStatistics.DumpEntry>();
	}

	public DumpStatistics(TraceStack traceStack) {
		super();
		this.traceStack = traceStack;
	}

	public void compute() {
		map = new HashMap<String, DumpEntry>();
		addMethodToMapCall(map, traceStack.entryMethod);
	}

	private void addMethodToMapCall(Map<String, DumpEntry> result, MethodTrace method) {
		// Adding only the call all along the path
		addMethodToMapCallSub(result, method.signature);
		for (MethodTrace methodTrace : method.subMethods) {
			addMethodToMapCall(result, methodTrace);
		}
	}

	private void addMethodToMapCallSub(Map<String, DumpEntry> result, String signature) {
		String realSig = signature;
		int indexOfSpace = signature.indexOf(' ');
		if (indexOfSpace >= 0) {
			int indexOfDot = signature.indexOf('.');
			if (indexOfSpace < indexOfDot) {
				realSig = signature.substring(indexOfSpace + 1);
			}
		}

		int indexOf = realSig.indexOf('.');
		if (indexOf == -1) {
			DumpEntry dumpEntry = getEntry(result, realSig);
			dumpEntry.value++;
		} else {
			String begin = realSig.substring(0, indexOf);
			String end = realSig.substring(indexOf + 1);
			DumpEntry dumpEntry = getEntry(result, begin);
			dumpEntry.value++;
			addMethodToMapCallSub(dumpEntry.subMap, end);
		}
	}

	private DumpEntry getEntry(Map<String, DumpEntry> result, String key) {
		DumpEntry entry = result.get(key);
		if (entry == null) {
			entry = new DumpEntry();
			result.put(key, entry);
		}
		return entry;
	}

	private final void log(String tolog) {
		System.out.println(traceStack.threadId + SPACE + tolog);
	}

	public void dump() {
		log(SEPARATOR);
		log(Messages.getString("TraceAnalyzer.THREAD_NAME") + traceStack.threadName); //$NON-NLS-1$
		log(Messages.getString("TraceAnalyzer.STACK_START") + new Date(traceStack.stackStart).toString()); //$NON-NLS-1$
		dumpMap(0, map);
		log(SEPARATOR);
	}

	private void dumpMap(int i, Map<String, DumpEntry> map) {
		Set<Entry<String, DumpEntry>> entrySet = map.entrySet();
		List<Entry<String, DumpEntry>> list = new ArrayList<Entry<String, DumpEntry>>(entrySet);
		Collections.sort(list, new Comparator<java.util.Map.Entry<String, DumpEntry>>() {
			@Override
			public int compare(Entry<String, DumpEntry> o1, Entry<String, DumpEntry> o2) {
				long l1 = o1.getValue().value;
				long l2 = o2.getValue().value;
				return l1 < l2 ? 1 : l1 == l2 ? 0 : -1;
			}
		});
		if (list.size() > 0) {
			long maxValue = list.get(list.size() - 1).getValue().value;
			int l = Long.valueOf(maxValue).toString().length();
			for (Entry<String, DumpEntry> entry : list) {
				System.out.print(tabs[i]);
				String v = Long.valueOf(entry.getValue().value).toString();
				for (int j = 0; j < (l - v.length()); j++) {
					System.out.print("0");
				}
				System.out.print(v);
				System.out.print(" ");
				System.out.println(entry.getKey());
				Map<String, DumpEntry> subMap = entry.getValue().subMap;
				dumpMap(i + l + 1, subMap);
			}
		}
	}
}
